﻿using System;
using System.Linq;

namespace CoreEx.Database.Query.Expressions
{
    /// <summary>
    /// A query factory class that is capable of creating Select, Update,Insert and Delete queries.
    /// </summary>
    /// <seealso cref="DbSelectQuery"/>    
    public static class DbQueryFactory
    {
        
        private static readonly DbExpressionFactory _expressionFactory = new DbExpressionFactory();

        /// <summary>
        /// Creates a <see cref="DbSelectQuery"/> used to retrieve data from the database.
        /// </summary>
        /// <param name="expressionSelector">A <see cref="Func{T,TResult}"/> used to specify the <see cref="DbExpression"/> that represents the projection.</param>
        /// <returns><see cref="DbSelectQuery"/></returns>
        public static DbQuery<DbSelectExpression> Select(params Func<DbExpressionFactory,DbExpression>[] expressionSelector)
        {
            return Select(_expressionFactory.List(expressionSelector.Select(e => e(_expressionFactory))));                        
        }

        /// <summary>
        /// Creates a <see cref="DbSelectQuery"/> used to retrieve data from the database.
        /// </summary>
        /// <param name="expression">A <see cref="DbExpression"/> that represents the projection.</param>
        /// <returns><see cref="DbSelectQuery"/></returns>
        public static DbQuery<DbSelectExpression> Select(DbExpression expression)
        {
            var selectQuery = new DbQuery<DbSelectExpression>();
            selectQuery.QueryExpression.ProjectionExpression = expression;
            return selectQuery;
        }

        /// <summary>
        /// Creates a <see cref="DbUpdateQuery"/> that is used to update data in the database.
        /// </summary>
        /// <param name="target">The <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns><see cref="DbUpdateQuery"/></returns>
        public static DbQuery<DbUpdateExpression> Update(DbExpression target)
        {
            var updateQuery = new DbQuery<DbUpdateExpression>();
            updateQuery.QueryExpression.Target = target;
            return updateQuery;
        }
        /// <summary>
        /// Creates a <see cref="DbUpdateQuery"/> that is used to update data in the database.
        /// </summary>
        /// <param name="targetSelector">A <see cref="Func{T,TResult}"/> used to 
        /// specify the <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns><see cref="DbUpdateQuery"/></returns>
        public static DbQuery<DbUpdateExpression> Update(Func<DbExpressionFactory, DbExpression> targetSelector)
        {
            return Update(targetSelector(_expressionFactory));
        }


        /// <summary>
        /// Creates a <see cref="DbDeleteQuery"/> that is used to delete data from the database.
        /// </summary>
        /// <param name="targetSelector">A <see cref="Func{T,TResult}"/> used to 
        /// specify the <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns><see cref="DbDeleteQuery"/></returns>
        public static DbQuery<DbDeleteExpression> Delete(Func<DbExpressionFactory, DbExpression> targetSelector)
        {
            return Delete(targetSelector(_expressionFactory));
        }


        /// <summary>
        /// Creates a <see cref="DbDeleteQuery"/> that is used to delete data from the database.
        /// </summary>
        /// <param name="target">The <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns></returns>
        public static DbQuery<DbDeleteExpression> Delete(DbExpression target)
        {
            var deleteQuery = new DbQuery<DbDeleteExpression>();
            deleteQuery.QueryExpression.Target = target;
            return deleteQuery;
        }

        /// <summary>
        /// Creates a <see cref="DbInsertQuery"/> that is used to insert data into the database.
        /// </summary>
        /// <param name="targetSelector">A <see cref="Func{T,TResult}"/> used to 
        /// specify the <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns></returns>
        public static DbQuery<DbInsertExpression> Insert(Func<DbExpressionFactory, DbExpression> targetSelector)
        {
            return Insert(targetSelector(_expressionFactory));
        }


        /// <summary>
        /// Creates a <see cref="DbInsertQuery"/> that is used to insert data into the database.
        /// </summary>
        /// <param name="target">The <see cref="DbExpression"/> that represents the target table or view.</param>
        /// <returns><see cref="DbInsertQuery"/></returns>
        public static DbQuery<DbInsertExpression> Insert(DbExpression target)
        {
            var insertQuery = new DbQuery<DbInsertExpression>();
            insertQuery.QueryExpression.Target = target;
            return insertQuery;
        }

        


    }

    /// <summary>
    /// Provides the common interface for implementing <see cref="DbQuery{TQueryExpression}"/>
    /// </summary>
    public abstract class DbQuery : DbExpression
    {
        ///// <summary>
        ///// Gets the <see cref="DbQueryType"/> for this <see cref="DbQuery{TQueryExpression}"/>.
        ///// </summary>
        //public abstract DbQueryType QueryType { get; }

        /// <summary>
        /// Returns the <see cref="DbExpression"/> that the <see cref="DbQuery{TQueryExpression}"/> implementation operates on.
        /// </summary>
        /// <remarks>
        /// This method may not be needed when proper covariance is added to the .Net framework.
        /// </remarks>
        /// <returns><see cref="DbExpression"/></returns>
        internal abstract DbQueryExpression GetQueryExpression();

    }


    /// <summary>
    /// Provides the basic fluent interface for all query types.
    /// </summary>
    /// <typeparam name="TQueryExpression"></typeparam>
    public class DbQuery<TQueryExpression> : 
        DbQuery where TQueryExpression:DbQueryExpression, new()
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="DbQuery{TQueryExpression}"/> class.
        /// </summary>
        internal DbQuery()
        {
            QueryExpression = new TQueryExpression();
        }

        /// <summary>
        /// Gets the <see cref="DbExpressionType"/> of the <see cref="DbExpression"/>.
        /// </summary>
        /// <value></value>
        public override DbExpressionType ExpressionType
        {
            get { return DbExpressionType.Query; }
        }

        

        /// <summary>
        /// Gets or sets the <see cref="DbQueryExpression"/> that this <see cref="DbQuery{TQueryExpression}"/> operates on.
        /// </summary>
        public TQueryExpression QueryExpression { get; set; }

      
        internal override DbQueryExpression GetQueryExpression()
        {
            return QueryExpression;
        }


        ///// <summary>
        ///// Specifies the tables, views, derived tables, and joined tables used in DELETE, SELECT, and UPDATE statements.
        ///// </summary>
        ///// <param name="expressionSelector">A <see cref="Func{T,TResult}"/> to specify the <see cref="DbExpression"/>.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> From(Func<DbExpressionFactory,DbExpression> expressionSelector)
        //{
        //    return From(expressionSelector(ExpressionFactory));            
        //}

        ///// <summary>
        ///// Specifies the tables, views, derived tables, and joined tables used in DELETE, SELECT, and UPDATE statements.
        ///// </summary>
        ///// <param name="expression">A <see cref="DbExpression"/> that represents the from clause.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> From(DbExpression expression)
        //{
        //    var dbExpression = expression;
        //    if (!QueryExpression.FromExpression.IsNull())
        //        dbExpression = ExpressionFactory.List(new[] { QueryExpression.FromExpression, expression });
        //    QueryExpression.FromExpression = dbExpression;
        //    return this;
        //}




        ///// <summary>
        ///// Creates a <see cref="DbJoinExpression"/> between the current 'FROM' clause and the expression returned by the <paramref name="target"/> functor.
        ///// </summary>
        ///// <param name="joinExpressionType">Specifies the type of join expression.</param>
        ///// <param name="target">The target join expression.</param>
        ///// <param name="condition">A <see cref="DbExpression"/> that specifies the join condition.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> Join(DbJoinExpressionType joinExpressionType, Func<DbExpressionFactory,DbExpression> target, Func<DbExpressionFactory,DbExpression> condition)
        //{
        //    var targetExpression = target(ExpressionFactory);
        //    var conditionExpression = condition(ExpressionFactory);
        //    var dbExpression = (DbExpression)ExpressionFactory.MakeJoin(joinExpressionType, targetExpression, conditionExpression);
        //    if (!QueryExpression.FromExpression.IsNull())
        //        dbExpression = ExpressionFactory.Concat(dbExpression, QueryExpression.FromExpression);
        //    QueryExpression.FromExpression = dbExpression;
        //    return this;
        //}

        ///// <summary>
        ///// Creates an 'INNER JOIN' between the current 'FROM' clause and the expression returned by the <paramref name="target"/> functor.
        ///// </summary>
        ///// <param name="target">The target join expression.</param>
        ///// <param name="condition">A <see cref="DbExpression"/> that specifies the join condition.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> InnerJoin(Func<DbExpressionFactory,DbExpression> target, Func<DbExpressionFactory,DbExpression> condition)
        //{
        //    return Join(DbJoinExpressionType.InnerJoin, target, condition);
        //}

        ///// <summary>
        ///// Creates an 'LEFT OUTER JOIN' between the current 'FROM' clause and the expression returned by the <paramref name="target"/> functor.
        ///// </summary>
        ///// <param name="target">The target join expression.</param>
        ///// <param name="condition">A <see cref="DbExpression"/> that specifies the join condition.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> LeftOuterJoin(Func<DbExpressionFactory, DbExpression> target, Func<DbExpressionFactory, DbExpression> condition)
        //{
        //    return Join(DbJoinExpressionType.LeftOuterJoin, target, condition);
        //}

        ///// <summary>
        ///// Creates an 'RIGHT OUTER JOIN' between the current 'FROM' clause and the expression returned by the <paramref name="target"/> functor.
        ///// </summary>
        ///// <param name="target">The target join expression.</param>
        ///// <param name="condition">A <see cref="DbExpression"/> that specifies the join condition.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> RightOuterJoin(Func<DbExpressionFactory, DbExpression> target, Func<DbExpressionFactory, DbExpression> condition)
        //{
        //    return Join(DbJoinExpressionType.LeftOuterJoin, target, condition);
        //}


        ///// <summary>
        ///// Specifies the search condition for the rows returned by the query.
        ///// </summary>
        ///// <param name="expressionSelector">A <see cref="Func{T,TResult}"/> to specify the <see cref="DbExpression"/>.</param>
        ///// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        //public DbQuery<TQueryExpression> Where(Func<DbExpressionFactory,DbExpression> expressionSelector)
        //{
        //    return Where(expressionSelector(ExpressionFactory));            
        //}

        ///// <summary>
        ///// Specifies the search condition for the rows returned by the query.
        ///// </summary>
        ///// <param name="dbExpression">The <see cref="DbExpression"/> that makes up the search condition.</param>
        ///// <returns></returns>
        //public DbQuery<TQueryExpression> Where(DbExpression dbExpression)
        //{            
        //    if (!QueryExpression.WhereExpression.IsNull())
        //        dbExpression = ExpressionFactory.And(QueryExpression.WhereExpression, dbExpression);
        //    QueryExpression.WhereExpression = dbExpression;
        //    return this;
        //}




    }
}
